16 Runtime运行时信息
写工具的时候,你可能会遇到这样的问题:
- 工具需要知道当前用户是谁,但不想把user_id作为参数暴露给LLM
- 工具需要连接数据库,但不想在全局变量里存连接对象
- 工具需要读写长期记忆,但不知道怎么获取store
Runtime就是来解决这类问题的——它是一个运行时上下文对象,让你在工具和中间件中安全地访问各种依赖信息,而不用硬编码或用全局状态。
打个比方:如果工具是工人,Runtime就是工人的"工具箱"——里面有用户信息、数据库连接、记忆存储等等,需要什么就从里面拿。
一、Runtime包含什么
Runtime对象里有五类信息:
| 信息 | 说明 | 用途 |
|---|---|---|
context | 用户自定义的静态上下文 | 用户ID、角色、配置等 |
store | 长期记忆存储 | 跨对话的持久化数据 |
execution_info | 执行信息 | thread_id、run_id |
server_info | 服务器信息 | 仅LangGraph Server环境可用 |
stream_writer | 流式写入器 | 自定义流式输出 |
二、基本用法
2.1 定义上下文
创建Agent时用context_schema定义上下文的结构,调用时传入具体的值:
python
from dataclasses import dataclass
from langchain.agents import create_agent
@dataclass
class Context:
user_id: str
user_role: str
agent = create_agent(
model="deepseek-v4-flash",
tools=[...],
context_schema=Context, # 定义上下文结构
)
# 调用时传入上下文
result = agent.invoke(
{"messages": [{"role": "user", "content": "查看我的订单"}]},
context=Context(user_id="user_123", user_role="admin"), # 传入具体值
)2.2 在工具中访问
通过ToolRuntime参数访问Runtime对象:
python
from dataclasses import dataclass
from langchain.tools import tool, ToolRuntime
@dataclass
class Context:
user_id: str
@tool
def get_my_orders(runtime: ToolRuntime[Context]) -> str:
"""获取当前用户的订单列表"""
user_id = runtime.context.user_id # 获取用户ID
# 用user_id查询数据库...
return f"用户 {user_id} 的订单: [...]"注意看,user_id不是工具的参数,LLM看不到也改不了它。它从Runtime中自动获取,这就是依赖注入的好处:
- LLM不会胡乱修改用户ID
- 工具的参数列表更简洁
- 测试时可以轻松注入不同的上下文
2.3 在中间件中访问
Node-style钩子通过Runtime参数访问,Wrap-style钩子通过ModelRequest.runtime访问:
python
from dataclasses import dataclass
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import before_model, wrap_model_call, ModelRequest, ModelResponse
from langgraph.runtime import Runtime
from typing import Callable
@dataclass
class Context:
user_name: str
user_role: str
# Node-style:通过runtime参数
@before_model
def log_request(state: AgentState, runtime: Runtime[Context]) -> dict | None:
print(f"[日志] 用户 {runtime.context.user_name} 发起请求")
return None
# Wrap-style:通过request.runtime
@wrap_model_call
def dynamic_prompt(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
user_name = request.runtime.context.user_name
user_role = request.runtime.context.user_role
system_prompt = f"你是助手,正在和 {user_name}({user_role})对话。"
modified_request = request.override(system_prompt=system_prompt)
return handler(modified_request)
agent = create_agent(
model="deepseek-v4-flash",
tools=[...],
middleware=[log_request, dynamic_prompt],
context_schema=Context,
)三、实用场景
3.1 动态提示词
根据用户身份注入不同的系统提示词:
python
from dataclasses import dataclass
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable
@dataclass
class Context:
user_role: str
@wrap_model_call
def role_based_prompt(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
role = request.runtime.context.user_role
prompts = {
"admin": "你是管理员助手,可以执行所有操作,包括删除和修改数据。",
"user": "你是普通用户助手,只能查询信息,不能执行修改操作。",
"guest": "你是访客助手,只能查看公开信息。",
}
system_prompt = prompts.get(role, prompts["guest"])
return handler(request.override(system_prompt=system_prompt))
agent = create_agent(
model="deepseek-v4-flash",
tools=[...],
middleware=[role_based_prompt],
context_schema=Context,
)3.2 权限控制
在中间件中根据用户角色限制工具使用:
python
from dataclasses import dataclass
from langchain.agents import create_agent, AgentState
from langchain.agents.middleware import wrap_model_call, ModelRequest, ModelResponse
from typing import Callable
@dataclass
class Context:
user_role: str
@wrap_model_call
def filter_tools_by_role(
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
role = request.runtime.context.user_role
# 根据角色过滤工具
if role == "guest":
# 访客只能用查询工具
allowed_tools = [t for t in request.tools if t.name.startswith("query_")]
return handler(request.override(tools=allowed_tools))
return handler(request)
agent = create_agent(
model="deepseek-v4-flash",
tools=[query_users, query_orders, delete_user, modify_order],
middleware=[filter_tools_by_role],
context_schema=Context,
)3.3 获取执行信息
通过runtime.execution_info获取当前执行的标识信息:
python
from langchain.tools import tool, ToolRuntime
@tool
def log_action(action: str, runtime: ToolRuntime) -> str:
"""记录操作日志"""
info = runtime.execution_info
print(f"[日志] Thread: {info.thread_id}, Run: {info.run_id}")
print(f"[日志] 操作: {action}")
return f"操作 '{action}' 已记录"3.4 访问长期记忆
通过runtime.store读写跨对话的持久化数据:
python
from langchain.tools import tool, ToolRuntime
@tool
def save_preference(key: str, value: str, runtime: ToolRuntime) -> str:
"""保存用户偏好设置"""
user_id = runtime.context.user_id
if runtime.store:
runtime.store.put(("preferences", user_id), key, {"value": value})
return f"偏好 '{key}' 已保存"
return "存储不可用"
@tool
def get_preference(key: str, runtime: ToolRuntime) -> str:
"""获取用户偏好设置"""
user_id = runtime.context.user_id
if runtime.store:
memory = runtime.store.get(("preferences", user_id), key)
if memory:
return f"{key}: {memory.value['value']}"
return f"未找到偏好 '{key}'"四、依赖注入的好处
用Runtime做依赖注入,比传统的全局变量或参数传递好在哪?
| 方式 | 问题 |
|---|---|
| 全局变量 | 线程不安全、难测试、耦合严重 |
| 工具参数 | LLM能看到和修改、参数列表膨胀 |
| Runtime注入 | 安全、可测试、解耦 |
用Runtime注入的代码更容易测试——测试时只需要传入不同的context就行:
python
# 生产环境
agent.invoke(
{"messages": [...]},
context=Context(user_id="real_user", user_role="admin"),
)
# 测试环境
agent.invoke(
{"messages": [...]},
context=Context(user_id="test_user", user_role="guest"),
)五、总结
Runtime是LangChain中实现依赖注入的核心机制:
- context:注入用户身份、配置等静态信息
- store:访问长期记忆存储
- execution_info:获取thread_id、run_id等执行标识
- 在工具中:通过
ToolRuntime参数访问 - 在中间件中:通过
Runtime参数或ModelRequest.runtime访问
用Runtime代替全局变量,让你的工具更安全、更灵活、更容易测试。
在下一篇文章中,我们将学习context上下文工程,了解如何精心设计Agent看到的信息。